const compileUtil = {
    getVal(expr,vm){
        console.log("DDDDDD")
        console.log(vm)
        return expr.split('.').reduce((data,currentVal)=>{
            // console.log(currentVal)
            return data[currentVal]
        },vm.$data)
    },
    setVal(expr,vm,inputVal){
        return expr.split('.').reduce((data,currentVal)=>{
            // console.log(currentVal)
            data[currentVal] = inputVal
        },vm.$data)
    },
    getContentVal(expr,vm){
        return expr.replace(/\{\{(.+?)\}\}/g,(...args)=>{
            return this.getVal(args[1],vm)
        })
    },
    text(node,expr,vm){ //expr:msg
        //const value =vm.$data[expr] 拿值不太合理 因为msg可以直接拿到，person.fav这种类型就拿不到所以定义一种方法
        // const value = this.getVal(expr,vm);
        // this.updater.textUpdater(node,value)
        let value
        if(expr.indexOf('{{') !== -1){   //{{person.name}} --{{person.age}}
            value = expr.replace(/\{\{(.+?)\}\}/g,(...args)=>{
                new watcher(vm,args[1],(newVal)=>{ //为啥是args[1]
                    this.updater.textUpdater(node,this.getContentVal(expr,vm))
                })
                return this.getVal(args[1],vm)
            })
        }else{
            value = this.getVal(expr,vm);
        }
         this.updater.textUpdater(node,value)
    },
    html(node,expr,vm){
        const value = this.getVal(expr,vm)

        //绑定观察者，将来数据发生变化 触发这里的回调 进行更新
        new watcher(vm,expr,(newVal)=>{
            this.updater.htmlUpdater(node,newVal)
        })
        this.updater.htmlUpdater(node,value)
    },
    model(node,expr,vm){
        const value = this.getVal(expr,vm)
        // 绑定更新函数 数据=>视图
        new watcher(vm,expr,(newVal)=>{
            this.updater.modelUpdater(node,newVal)
        })
        // 视图=>数据=>视图
        node.addEventListener('input',(e)=>{
            //设置值
            this.setVal(expr,vm,e.target.value)
        })
        this.updater.modelUpdater(node,value)
    },
    on(node,expr,vm,eventName){
        let fn = vm.$options.methods && vm.$options.methods[expr];
        node.addEventListener(eventName,fn.bind(vm),false)
    },
    bind(node,expr,vm,eventName){

    },
    //更新的函数
    updater:{
        textUpdater(node,value){
            node.textContent = value;
        },
        htmlUpdater(node,value){
            node.innerHTML = value
        },
        modelUpdater(node,value){
            node.value = value
        }
    }

}
class Compile{
    constructor(el,vm){
        this.el = this.isElementNode(el) ? el : document.querySelector(el);
        this.vm = vm;
        console.log("获取compile的对象")
        console.log(this.vm)
        //1.获取文档碎片对象 放入内存中会减少页面的回流和重绘
        const fragment = this.node2Fragment(this.el)
        console.log(fragment)
        //2追加子元素到根元素
        // this.el.appendChild(fragment)
        //ture 2. 编译模板
        this.compile(fragment)
        //3追加子元素到根元素
         this.el.appendChild(fragment)
    
    }
    //编译模板
    compile(fragment){
        //获取到每一个节点
        console.log("W0cao")
        console.log(fragment)
        const childNodes = fragment.childNodes;
        [...childNodes].forEach(child =>{
            console.log(child)
            if(this.isElementNode(child)){
                //是元素节点
                // 编译元素节点
                console.log('元素节点',child)
                this.compileElement(child)
            }else{
                console.log('文本节点',child)
                this.compileText(child)
            }
            if(child.childNodes && child.childNodes.length){
                this.compile(child);
            }
        })
    }
    compileElement(node){
       // console.log(node)
        // <div v-text='msg'></div>
        const attributes = node.attributes;
        // console.log(attributes)
       [...attributes].forEach(attr =>{
        //console.log(attr)    v-text = 'msg'
            const {name,value} = attr
            // console.log(name)
            // console.log(value)
            if(this.isDirective(name)){//是一个指令  v-text v-html v-model v-on:click
                const [,directive] = name.split('-'); //text html model on:click
                const [dirname,eventName] = directive.split(':'); //text html model on
                //更新数据 数据驱动视图
                compileUtil[dirname](node,value,this.vm,eventName)
                // 删除有指令的标签上的属性
                node.removeAttribute('v-'+directive)
            }else if(this.isEventName(name)){ //@click = 'handleClick'
                let [,eventName] = name.split('@');
                compileUtil['on'](node,value,this.vm,eventName)
            }
       })
    }
    compileText(node){
        // console.log(node)
        //console.log(node.textContent)
        //{{}} v-text
        const content = node.textContent;
        console.log("TXT")
        console.log(this.vm)
        if(/\{\{(.+?)\}\}/.test(content)){
            //console.log(content)
            compileUtil['text'](node,content,this.vm)
        }
    }
    isEventName(attrName){
        return attrName.startsWith('@');
    }
    isDirective(attrName) {
        return attrName.startsWith('v-')
    }
    node2Fragment(el){  
        //创建文档碎片
        const f = document.createDocumentFragment();
        let firstChild;
        while(firstChild = el.firstChild){
            // console.log(firstChild)
            f.appendChild(firstChild)
        }
        return f
    }   


    isElementNode(node){
        return node.nodeType === 1;
    }
}
class LIUVue {
    constructor (options){
        this.$el = options.el
        this.$data = options.data
        this.$options = options
        if(this.$el){
            //观察者
            new Observer(this.$data)
            //解析器
            new Compile(this.$el,this)
            this.proxyData(this.$data)
        }
    }
    proxyData(data){
        for(const key in data){
            Object.defineProperties(this,key,{
                get(){
                    return data[key]
                },
                set(newVal){
                    return data[key] = newVal
                }
            })
        }

    }
}